package com.apobates.forum.nanus.core.session;

import com.apobates.forum.nanus.NanusBean;
import com.apobates.forum.nanus.NanusHashStrategy;
import com.apobates.forum.nanus.core.AbstractNanusStorage;
import com.apobates.forum.nanus.NanusConverter;
import com.apobates.forum.nanus.NanusStorageConfig;
import com.apobates.forum.nanus.core.NanusStoragePayload;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Session的存储方案
 * @author xiaofanku@live.cn
 * @since 20210827
 */
public class NanusSessionStorageProvider extends AbstractNanusStorage {
    private final static Logger logger = LoggerFactory.getLogger(NanusSessionStorageProvider.class);

    public NanusSessionStorageProvider(NanusConverter beanConverter, NanusStorageConfig expirationConfig, NanusHashStrategy hashStrategy) {
        super(beanConverter, expirationConfig, hashStrategy);
    }

    @Override
    public void store(NanusBean bean, HttpServletRequest request, HttpServletResponse response) {
        //计算载荷
        Map<String,String> payload = getBeanConverter().toMap(bean);
        String serialJSON = new Gson().toJson(payload);
        //int stamp, String factor, String hash
        NanusStoragePayload nsp = new NanusStoragePayload(
                getUnixStamp(),
                getFactor(request),
                encode(serialJSON, NP));
        //ETC
        request.getSession().invalidate();
        HttpSession s = request.getSession(true);
        s.setAttribute(getStorageConfig().getKeyName(),  nsp);
    }

    @Override
    public void delete(HttpServletRequest request, HttpServletResponse response) {
        HttpSession s = request.getSession();
        s.removeAttribute(getStorageConfig().getKeyName());
        s.invalidate();
    }

    @Override
    public Optional<NanusBean> getInstance(HttpServletRequest request) {
        NanusBean ins=null;
        try{
            NanusStoragePayload nsp = parsePayload(request).orElse(null);
            if(null == nsp){
                return Optional.empty();
            }
            //是否到期了
            if(nsp.getStamp() < getUnixStamp()){
                return Optional.empty();
            }
            Map<String, String> payload = toPayloadMap(nsp);
            ins = getBeanConverter().toBean(payload);
        }catch(Exception e){
            if(logger.isDebugEnabled()){
                logger.debug("[Nanus]get session msb fail, exception: "+e.getMessage(), e);
            }
        }
        return Optional.ofNullable(ins);
    }

    @Override
    protected Optional<NanusStoragePayload> parsePayload(HttpServletRequest request) {
        NanusStoragePayload nsp = (NanusStoragePayload) request.getSession().getAttribute(getStorageConfig().getKeyName());
        return Optional.ofNullable(nsp);
    }

    @Override
    protected Map<String, String> toPayloadMap(NanusStoragePayload nsp) {
        Objects.requireNonNull(nsp);
        String serialJSON = decode(nsp.getHash(), NP);
        Map<String,String> payload = new Gson().fromJson(serialJSON, new TypeToken<Map<String,String>>(){}.getType());
        return payload;
    }

    @Override
    public void refresh(HttpServletRequest request, HttpServletResponse response, NanusBean newBean) {
        NanusStoragePayload currentNsp = parsePayload(request).orElse(null);
        Map<String, String> newPayload = mergeBeanMap(toPayloadMap(currentNsp), newBean);
        if(newPayload.isEmpty()){
            return;
        }
        String serialJSON = new Gson().toJson(newPayload);
        //使用原来的到期日期
        NanusStoragePayload nsp=new NanusStoragePayload(
                getUnixStamp(),
                getFactor(request),
                encode(serialJSON, NP));
        //ETC
        request.getSession().invalidate();
        HttpSession s = request.getSession(true);
        s.setAttribute(getStorageConfig().getKeyName(),  nsp);
    }

    @Override
    public boolean isSupportRevival() {
        return true;
    }
}
